package bg.btanev.eightyfour.client.presenter;

import java.util.ArrayList;
import java.util.List;

import bg.btanev.eightyfour.client.service.GWTCommandServiceAsync;
import bg.btanev.eightyfour.shared.analytics.DayActivityStat;
import bg.btanev.eightyfour.shared.analytics.HourActivityStat;
import bg.btanev.eightyfour.shared.analytics.WeekDayActivityStat;
import bg.btanev.eightyfour.shared.command.CommandResult;
import bg.btanev.eightyfour.shared.command.stat.StatCommandInput;
import bg.btanev.eightyfour.shared.command.stat.StatDayActivityCommand;
import bg.btanev.eightyfour.shared.command.stat.StatHourActivityCommand;
import bg.btanev.eightyfour.shared.command.stat.StatSummaryCommand;
import bg.btanev.eightyfour.shared.command.stat.StatSummaryCommandResult;
import bg.btanev.eightyfour.shared.command.stat.StatWeekDayActivityCommand;
import bg.btanev.eightyfour.shared.dto.TargetDto;
import ca.nanometrics.gflot.client.Axis;
import ca.nanometrics.gflot.client.DataPoint;
import ca.nanometrics.gflot.client.PlotModel;
import ca.nanometrics.gflot.client.PlotModelStrategy;
import ca.nanometrics.gflot.client.PlotWithOverview;
import ca.nanometrics.gflot.client.PlotWithOverviewModel;
import ca.nanometrics.gflot.client.SeriesHandler;
import ca.nanometrics.gflot.client.SimplePlot;
import ca.nanometrics.gflot.client.options.AxisOptions;
import ca.nanometrics.gflot.client.options.BarSeriesOptions;
import ca.nanometrics.gflot.client.options.LegendOptions;
import ca.nanometrics.gflot.client.options.LineSeriesOptions;
import ca.nanometrics.gflot.client.options.PlotOptions;
import ca.nanometrics.gflot.client.options.PointsSeriesOptions;
import ca.nanometrics.gflot.client.options.TickFormatter;
import ca.nanometrics.gflot.client.options.BarSeriesOptions.BarAlignment;

import com.google.gwt.event.dom.client.ChangeEvent;
import com.google.gwt.event.dom.client.ChangeHandler;
import com.google.gwt.event.dom.client.HasChangeHandlers;
import com.google.gwt.i18n.client.DateTimeFormat;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.HasWidgets;
import com.google.gwt.user.client.ui.Label;

/**
 *
 * @author B.Tanev
 *
 */
public class StatsListPresenter implements Presenter {

	private static final String STAT_YEAR_ACTIVITY = "Year Activity";
	private static final String STAT_MONTH_ACTIVITY = "Month Activity";
	private static final String STAT_HOUR_ACTIVITY = "Hour Activity";
	private static final String STAT_DAY_ACTIVITY = "Day Activity";
	private static final String STAT_WEEK_DAY_ACTIVITY = "Week day activity";
	private static final String STAT_SUMMARY = "Summary";

	private static final String[] STATS_MENU = new String[] { STAT_SUMMARY, STAT_HOUR_ACTIVITY, STAT_DAY_ACTIVITY,
			STAT_WEEK_DAY_ACTIVITY };

	public static interface StatsListDisplay extends Display {
		void clearView();

		void setSelectionData(String[] statsSelection);

		String getSelectedStat();

		HasChangeHandlers getSelectionData();

		HasWidgets getStatsDataContainer();
	}

	@SuppressWarnings("unchecked")
	private final GWTCommandServiceAsync rpcService;
	private final StatsListDisplay display;
	private final TargetDto target;

	@SuppressWarnings("unchecked")
	public StatsListPresenter(GWTCommandServiceAsync rpcService, StatsListDisplay display, TargetDto target) {
		super();

		this.rpcService = rpcService;
		this.display = display;
		this.target = target;

		this.display.getSelectionData().addChangeHandler(new ChangeHandler() {

			@Override
			public void onChange(ChangeEvent event) {
				String stat = StatsListPresenter.this.display.getSelectedStat();
				if (stat != null) {
					loadStat(stat);
				}
			}
		});
	}

	protected void loadStat(String stat) {
		Presenter presenter = null;

		if (STAT_SUMMARY.equals(stat)) {
			showSummary();
		} else if (STAT_HOUR_ACTIVITY.equals(stat)) {
			showHourActivity();
		} else if (STAT_DAY_ACTIVITY.equals(stat)) {
			showDayActovity();
		} else if (STAT_WEEK_DAY_ACTIVITY.equals(stat)) {
			showWeekDayActivity();
		} else if (STAT_MONTH_ACTIVITY.equals(stat)) {
			// TODO
		} else if (STAT_YEAR_ACTIVITY.equals(stat)) {
			// TODO
		}

		if (presenter != null) {
			presenter.go(this.display.getStatsDataContainer());
		}
	}

	@SuppressWarnings("unchecked")
	private void showDayActovity() {
		HasWidgets dataContainer = this.display.getStatsDataContainer();
		dataContainer.clear();
		dataContainer.add(new Label("Loading.."));

		StatCommandInput input = new StatCommandInput();
		input.setTargetDto(target);
		StatDayActivityCommand command = new StatDayActivityCommand();
		command.setInput(input);

		rpcService.executeCommand(command,
				new AsyncCallback<CommandResult<StatCommandInput, ArrayList<DayActivityStat>>>() {

					@Override
					public void onFailure(Throwable caught) {
						HasWidgets dataContainer = display.getStatsDataContainer();
						dataContainer.clear();
						dataContainer.add(new Label("Error..."));
					}

					@Override
					public void onSuccess(CommandResult<StatCommandInput, ArrayList<DayActivityStat>> result) {
						HasWidgets dataContainer = display.getStatsDataContainer();
						dataContainer.clear();

						List<DayActivityStat> hoursStatsResult = null;
						if (result != null) {
							hoursStatsResult = result.getResult();
						}

						if (hoursStatsResult == null) {
							return;
						}

						showDayStatsGraph(dataContainer, hoursStatsResult);
					}

				});
	}

	private void showDayStatsGraph(HasWidgets dataContainer, final List<DayActivityStat> dayStatsResult) {
		PlotWithOverviewModel model = new PlotWithOverviewModel(PlotModelStrategy.defaultStrategy());
		PlotOptions plotOptions = new PlotOptions();
		plotOptions.setDefaultLineSeriesOptions(new LineSeriesOptions().setLineWidth(1).setShow(true));
		plotOptions.setDefaultPointsOptions(new PointsSeriesOptions().setRadius(2).setShow(true));
		plotOptions.setDefaultShadowSize(0);
		plotOptions.setXAxisOptions(new AxisOptions().setTickFormatter(new TickFormatter() {

			@Override
			public String formatTickValue(double tickValue, Axis axis) {
				try {
					DayActivityStat dayActivityStat = dayStatsResult.get((int) tickValue);
					DateTimeFormat formatter = DateTimeFormat.getShortDateFormat();
					return formatter.format(dayActivityStat.getDay());
				} catch (Exception e) {
					return "";
				}
			}
		}));

		SeriesHandler series = model.addSeries("Day activity", "blue");

		for (int i = 0; i < dayStatsResult.size(); i++) {
			series.add(new DataPoint(i, dayStatsResult.get(i).getTotal()));
		}

		// create the plot
		final PlotWithOverview plot = new PlotWithOverview(model, plotOptions);

		dataContainer.add(plot);
	}

	@SuppressWarnings("unchecked")
	private void showHourActivity() {
		HasWidgets dataContainer = this.display.getStatsDataContainer();
		dataContainer.clear();
		dataContainer.add(new Label("Loading.."));

		StatCommandInput input = new StatCommandInput();
		input.setTargetDto(target);
		StatHourActivityCommand command = new StatHourActivityCommand();
		command.setInput(input);

		rpcService.executeCommand(command,
				new AsyncCallback<CommandResult<StatCommandInput, ArrayList<HourActivityStat>>>() {

					@Override
					public void onFailure(Throwable caught) {
						HasWidgets dataContainer = display.getStatsDataContainer();
						dataContainer.clear();
						dataContainer.add(new Label("Error..."));
					}

					@Override
					public void onSuccess(CommandResult<StatCommandInput, ArrayList<HourActivityStat>> result) {
						HasWidgets dataContainer = display.getStatsDataContainer();
						dataContainer.clear();

						List<HourActivityStat> hoursStatsResult = null;
						if (result != null) {
							hoursStatsResult = result.getResult();
						}

						if (hoursStatsResult == null) {
							return;
						}

						showHourStatsGraph(dataContainer, hoursStatsResult);
					}
				});
	}

	private void showHourStatsGraph(HasWidgets dataContainer, List<HourActivityStat> hoursStatsResult) {
		PlotModel model = new PlotModel();
		PlotOptions plotOptions = new PlotOptions();

		BarSeriesOptions barSeriesOptions = new BarSeriesOptions();
		barSeriesOptions.setShow(true);
		barSeriesOptions.setLineWidth(1);
		barSeriesOptions.setBarWidth(1);
		barSeriesOptions.setAlignment(BarAlignment.CENTER);

		plotOptions.setDefaultBarsSeriesOptions(barSeriesOptions);
		plotOptions.setLegendOptions(new LegendOptions().setShow(false));

		// add tick formatter to the options
		plotOptions.setXAxisOptions(new AxisOptions().setTicks(24).setTickFormatter(new TickFormatter() {
			public String formatTickValue(double tickValue, Axis axis) {
				return "" + (int) tickValue;
			}
		}));

		// create a series
		SeriesHandler handler = model.addSeries("Hour stat", "blue");

		// add data
		for (HourActivityStat statItem : hoursStatsResult) {
			handler.add(new DataPoint(statItem.getHour(), statItem.getTotal()));
		}

		// create the plot
		SimplePlot plot = new SimplePlot(model, plotOptions);

		dataContainer.add(plot);
	}

	@SuppressWarnings("unchecked")
	private void showWeekDayActivity() {
		HasWidgets dataContainer = this.display.getStatsDataContainer();
		dataContainer.clear();
		dataContainer.add(new Label("Loading.."));

		StatCommandInput input = new StatCommandInput();
		input.setTargetDto(target);
		StatWeekDayActivityCommand command = new StatWeekDayActivityCommand();
		command.setInput(input);

		rpcService.executeCommand(command,
				new AsyncCallback<CommandResult<StatCommandInput, ArrayList<WeekDayActivityStat>>>() {

					@Override
					public void onFailure(Throwable caught) {
						HasWidgets dataContainer = display.getStatsDataContainer();
						dataContainer.clear();
						dataContainer.add(new Label("Error..."));
					}

					@Override
					public void onSuccess(CommandResult<StatCommandInput, ArrayList<WeekDayActivityStat>> result) {
						HasWidgets dataContainer = display.getStatsDataContainer();
						dataContainer.clear();

						List<WeekDayActivityStat> weekDayStatsResult = null;
						if (result != null) {
							weekDayStatsResult = result.getResult();
						}

						if (weekDayStatsResult == null) {
							return;
						}

						showWeekDayStatsGraph(dataContainer, weekDayStatsResult);
					}
				});
	}

	private void showWeekDayStatsGraph(HasWidgets dataContainer, List<WeekDayActivityStat> weekDayStatsResult) {
		PlotModel model = new PlotModel();
		PlotOptions plotOptions = new PlotOptions();

		BarSeriesOptions barSeriesOptions = new BarSeriesOptions();
		barSeriesOptions.setShow(true);
		barSeriesOptions.setLineWidth(1);
		barSeriesOptions.setBarWidth(1);
		barSeriesOptions.setAlignment(BarAlignment.CENTER);

		plotOptions.setDefaultBarsSeriesOptions(barSeriesOptions);
		plotOptions.setLegendOptions(new LegendOptions().setShow(false));

		// add tick formatter to the options
		plotOptions.setXAxisOptions(new AxisOptions().setTicks(7).setTickFormatter(new TickFormatter() {
			public String formatTickValue(double tickValue, Axis axis) {
				String day = "";
				switch ((int) tickValue) {
					case 0:
						day = "Sunday";
						break;
					case 1:
						day = "Monday";
						break;
					case 2:
						day = "Tuesday";
						break;
					case 3:
						day = "Wednesday";
						break;
					case 4:
						day = "Thursday";
						break;
					case 5:
						day = "Friday";
						break;
					case 6:
						day = "Saturday";
						break;
				}

				return day;
			}
		}));

		// create a series
		SeriesHandler handler = model.addSeries("Hour stat", "blue");

		// add data
		for (WeekDayActivityStat statItem : weekDayStatsResult) {
			handler.add(new DataPoint(statItem.getWeekDay(), statItem.getTotal()));
		}

		// create the plot
		SimplePlot plot = new SimplePlot(model, plotOptions);

		dataContainer.add(plot);
	}

	@SuppressWarnings("unchecked")
	private void showSummary() {
		HasWidgets dataContainer = this.display.getStatsDataContainer();
		dataContainer.clear();
		dataContainer.add(new Label("Loading.."));

		StatCommandInput input = new StatCommandInput();
		input.setTargetDto(target);
		StatSummaryCommand command = new StatSummaryCommand();
		command.setInput(input);

		rpcService.executeCommand(command,
				new AsyncCallback<CommandResult<StatCommandInput, StatSummaryCommandResult>>() {

					@Override
					public void onFailure(Throwable caught) {
						HasWidgets dataContainer = display.getStatsDataContainer();
						dataContainer.clear();
						dataContainer.add(new Label("Error..."));
					}

					@Override
					public void onSuccess(CommandResult<StatCommandInput, StatSummaryCommandResult> result) {
						HasWidgets dataContainer = display.getStatsDataContainer();
						dataContainer.clear();

						StatSummaryCommandResult summaryResult = null;
						if (result != null) {
							summaryResult = result.getResult();
						}

						if (summaryResult == null) {
							return;
						}

						Grid dataGrid = new Grid(9, 2);
						dataGrid.setWidget(0, 0, new Label("profiles count"));
						dataGrid.setWidget(0, 1, new Label("" + summaryResult.getTotalProfilesCount()));

						dataGrid.setWidget(1, 0, new Label("most active profile"));
						dataGrid.setWidget(1, 1, new Label(summaryResult.getMostActiveProfile().getName()));

						dataGrid.setWidget(2, 0, new Label("profile events count"));
						dataGrid.setWidget(2, 1, new Label("" + summaryResult.getProfileEventsCount()));

						dataGrid.setWidget(3, 0, new Label("total profile events count"));
						dataGrid.setWidget(3, 1, new Label("" + summaryResult.getTotalProfileEventsCount()));

						dataGrid.setWidget(4, 0, new Label("total tagged profile events"));
						dataGrid.setWidget(4, 1, new Label("" + summaryResult.getTotalTaggedProfileEvents()));

						dataGrid.setWidget(5, 0, new Label("total tags used"));
						dataGrid.setWidget(5, 1, new Label("" + summaryResult.getTotalTagsUsed()));

						dataGrid.setWidget(6, 0, new Label("total unique tags used"));
						dataGrid.setWidget(6, 1, new Label("" + summaryResult.getTotalUniqueTagsUsed()));

						dataGrid.setWidget(7, 0, new Label("most common tag"));
						dataGrid.setWidget(7, 1, new Label(summaryResult.getMostCommonTag().getId()));

						dataGrid.setWidget(8, 0, new Label("most common tag use count"));
						dataGrid.setWidget(8, 1, new Label("" + summaryResult.getMostCommonTagProfilesEventsCount()));

						dataContainer.add(dataGrid);
					}
				});
	}

	@Override
	public void go(HasWidgets container) {
		container.clear();
		display.clearView();
		display.setSelectionData(STATS_MENU);
		container.add(display.asWidget());
	}
}
